package annotation;

import java.lang.reflect.Proxy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;

import proxy.IRequestProxy;

/**
 * 自定义 注解EnableFeignClientsCustomer 工厂类 用于生成匿名代理类
 *
 * @author wxf
 */
public class FeignClientFactoryBeanCustomer<T>
		implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, EnvironmentAware {
	/**
	 * 生成类的类型
	 */
	private Class<?> type;
	/**
	 * 注解 @FeignClient 中 value值 即serviceId服务提供者的容器id
	 *
	 * @see FeignClient 此注解未引入
	 */
	private String name;
	private ApplicationContext applicationContext;
	private Object proxyObj;
	private Environment environment;

	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.hasText(this.name, "Name must be set");
	}

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}

	@Override
	public void setApplicationContext(ApplicationContext context) throws BeansException {
		this.applicationContext = context;
	}

	/**
	 * 返回由工厂类生成的bean代理类
	 */
	@Override
	public Object getObject() throws Exception {
		IRequestProxy iRequestProxy = applicationContext.getBean(IRequestProxy.class);
		proxyObj = Proxy.newProxyInstance(this.type.getClassLoader(), new Class[] { this.type },
				(proxy, method, args) -> {
					ProxyMethod proxyMethod = new ProxyMethod(method, iRequestProxy);
					String serviceId = environment.getProperty(name);
					return proxyMethod.getProxy(args, serviceId);
				});
		return proxyObj;
	}

	@Override
	public Class<?> getObjectType() {
		return this.type;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}

	public Class<?> getType() {
		return type;
	}

	public void setType(Class<?> type) {
		this.type = type;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}
